<?php

/*
 * Copyright (c) 2009, Nguyen Duc Thuan <me@ndthuan.com>
 * All rights reserved.
 */

class Fete_Cache
{
    /**
     *
     * @var Fete_Cache
     */
    protected static $_instance = null;

    /**
     *
     * @var string
     */
    protected $_dir = null;

    /**
     * @return Fete_Cache
     */
    static public function &getInstance($dir = null)
    {
        if (null === self::$_instance) {
            self::$_instance = new self($dir);
        }

        return self::$_instance;
    }

    /**
     *
     * @param string $dir
     * @return Fete_Cache
     */
    public function &setCacheDir($dir)
    {
        $this->_dir = $dir;

        return $this;
    }

    /**
     *
     * @param string $key
     * @param mixed $value
     * @return Fete_Cache
     */
    public function &set($key, $value)
    {
        if (!preg_match('#^[\w\d-]+$#i', $key)) {
            throw new Fete_Cache_Exception('Invalid cache key');
            return $this;
        }

        $cacheFile = $this->_dir . DIRECTORY_SEPARATOR . $key;
        file_put_contents($cacheFile, serialize($value));
        unset($cacheFile);
        return $this;
    }

    /**
     *
     * @param string $key
     * @param callback $callback a function to call when the cache value is not
     * yet set
     * @param integer $time number of seconds of cache if the key is not yet set
     * @return mixed
     */
    public function get($key, $callback = null, $time = 3600)
    {
        $cacheFile = $this->_dir . DIRECTORY_SEPARATOR . $key;
        if (file_exists($cacheFile)
            && filemtime($cacheFile) >= (time() - $time)) {
            return unserialize(file_get_contents($cacheFile));
        }

        if (null !== $callback) {
            if (is_callable($callback)) {
                $value = call_user_func($callback);
            } else {
                $value = eval("return $callback;");
            }
            $this->set($key, $value);
            return $value;
        }

        return null;
    }

    protected function __construct($dir = null)
    {
        if (null !== $this->_dir) {
            $this->_dir = $dir;
        }
    }

    private function __clone() {}
}
